/*
 *  Copyright (c) 2011 Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the GNU Lesser Public License v2.1
 *  which accompanies this distribution, and is available at
 *  http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 *
 *  Contributors:
 *      Leibniz Institute of Plant Genetics and Crop Plant Research (IPK), Gatersleben, Germany - RMI Client, FileChooser and WebDAV
 */
package de.ipk_gatersleben.bit.bi.edal.rmi.client;

import java.rmi.ConnectIOException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.security.Principal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.security.auth.Subject;

import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;

import de.ipk_gatersleben.bit.bi.edal.aspectj.security.GrantableMethods.Methods;
import de.ipk_gatersleben.bit.bi.edal.primary_data.EdalConfiguration;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.PrimaryDataDirectoryException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.implementation.ALLPrincipal;
import de.ipk_gatersleben.bit.bi.edal.primary_data.file.EdalException;
import de.ipk_gatersleben.bit.bi.edal.primary_data.metadata.MetaData;
import de.ipk_gatersleben.bit.bi.edal.primary_data.security.EdalAuthenticateException;
import de.ipk_gatersleben.bit.bi.edal.rmi.interfaces.DataManagerRmiInterface;
import de.ipk_gatersleben.bit.bi.edal.rmi.interfaces.PrimaryDataDirectoryRmiInterface;
import de.ipk_gatersleben.bit.bi.edal.rmi.server.Authentication;
import de.ipk_gatersleben.bit.bi.edal.rmi.server.EdalServer;
import de.ipk_gatersleben.bit.bi.edal.rmi.server.ssl.EdalSslRmiClientSocketFactory;

/**
 * Class that provides the connection to an eDAL RMI server.
 * 
 * @author arendd
 */
public class ClientDataManager {

	public static Logger logger = null;

	static {
		DOMConfigurator.configure(ClientDataManager.class
				.getResource("log4j.xml"));
		logger = Logger.getLogger("eDAL-Client");

		ClientDataManager.subject = null;
		ClientDataManager.dataManager = null;
	}

	private static Subject subject;

	private static DataManagerRmiInterface dataManager;

	private static Map<Principal, List<Methods>> userPermissions;

	/**
	 * Do nothing. Just to force the Java Class loader to load the class and run
	 * the static block to initialize the logging system.
	 */
	public static void init() {

	}

	private Authentication auth;

	/**
	 * Constant map with all initial default permissions.<br/>
	 * 
	 * {@link ALLPrincipal}:<br/>
	 * Methods.listPrimaryDataEntities<br/>
	 * Methods.getPrimaryDataEntity<br/>
	 * Methods.read<br/>
	 * Methods.exist<br/>
	 * Methods.getParentDirectory<br/>
	 * Methods.getVersions<br/>
	 * Methods.getCurrentVersion<br/>
	 * Methods.searchByDublinCoreElement<br/>
	 * Methods.searchByMetaData<br/>
	 */
	public static Map<Principal, List<Methods>> DEFAULT_PERMISSIONS = new HashMap<Principal, List<Methods>>();

	private final int port;

	private final String host;

	/**
	 * Constructor for {@link ClientDataManager}.
	 * 
	 * @param auth
	 *            the {@link Authentication} object.
	 * @param port
	 *            the port to the eDAL server.
	 * @param host
	 *            the path of the eDAL server.
	 * @throws EdalAuthenticateException
	 *             if the {@link Subject} is null.
	 */
	public ClientDataManager(final int port, final String host,
			Authentication auth) throws EdalAuthenticateException {

		this.auth = auth;
		this.port = port;
		this.host = host;
	}

	/**
	 * Create a new {@link MetaData} instance with default values.
	 * 
	 * @return a new {@link MetaData} object.
	 */
	public MetaData createMetadataInstance() {
		MetaData metadata = null;
		try {
			metadata = ClientDataManager.dataManager.createMetaDataInstance();
		} catch (final RemoteException e) {
			ClientDataManager.logger.error(e.getMessage());
		}
		return metadata;
	}

	public Authentication getAuthentication() {
		return this.auth;

	}

	/**
	 * Getter for the available space in the mount path of eDAL.
	 * 
	 * @return available space
	 * @throws RemoteException
	 *             if unable to call remote function.
	 * @throws EdalException
	 *             if no mount path is set.
	 */
	public Long getAvailableStorageSpace() throws RemoteException,
			EdalException {
		return ClientDataManager.dataManager.getAvailableStorageSpace();
	}

	/**
	 * Get the local stored default permissions for the current client user.
	 * 
	 * @return the {@link Map} with the default permissions.
	 */
	public Map<Principal, List<Methods>> getDefaultPermissions() {
		return ClientDataManager.userPermissions;
	}

	/**
	 * @return the host
	 */
	public String getHost() {
		return host;
	}

	/**
	 * @return the port
	 */
	public int getPort() {
		return port;
	}

	/**
	 * Central entry point. Connect the client to the eDAL system and provide
	 * the root {@link ClientPrimaryDataDirectory}.
	 * 
	 * @return the root {@link ClientPrimaryDataDirectory} object.
	 * @throws RemoteException
	 *             if unable to call remote function.
	 * @throws NotBoundException
	 *             if no {@link DataManagerRmiInterface} is bound.
	 * @throws PrimaryDataDirectoryException
	 *             if unable to load the rootDirectory.
	 * @throws EdalException
	 * @throws EdalAuthenticateException
	 */
	public ClientPrimaryDataDirectory getRootDirectory()
			throws RemoteException, NotBoundException,
			PrimaryDataDirectoryException, EdalException,
			EdalAuthenticateException {

		/**
		 * it is important to reload the log4j configuration again, because it
		 * will be overwritten by the EdalServer, when calling lookup.
		 */
		DOMConfigurator.configure(ClientDataManager.class
				.getResource("log4j.xml"));
		logger = Logger.getLogger("eDAL-Client");

		Registry registry = null;
		try {
			logger.info("trying unsecure Connection...");

			registry = LocateRegistry.getRegistry(host, port);

			ClientDataManager.dataManager = (DataManagerRmiInterface) registry
					.lookup(EdalServer.DATA_MANAGER_NAME);
			/**
			 * it is important to reload the log4j configuration again, because
			 * it will be overwritten by the EdalServer, when calling lookup.
			 */
			DOMConfigurator.configure(ClientDataManager.class
					.getResource("log4j.xml"));
			logger = Logger.getLogger("eDAL-Client");

			logger.info("unsecure Connection successful !");

		} catch (ConnectIOException e) {
			logger.info("unsecure Connection failed !");
			logger.info("trying SSL Connection...");

			registry = LocateRegistry.getRegistry(host, port,
					new EdalSslRmiClientSocketFactory(
							EdalConfiguration.KEY_STORE_PATH));
			ClientDataManager.dataManager = (DataManagerRmiInterface) registry
					.lookup(EdalServer.DATA_MANAGER_NAME);
			/**
			 * it is important to reload the log4j configuration again, because
			 * it will be overwritten by the EdalServer, when calling lookup.
			 */
			DOMConfigurator.configure(ClientDataManager.class
					.getResource("log4j.xml"));
			logger = Logger.getLogger("eDAL-Client");

			logger.info("secure Connection successful !");

		}

		ClientDataManager.subject = ClientDataManager.dataManager
				.authenticate(this.auth);

		final PrimaryDataDirectoryRmiInterface rootDirectory = ClientDataManager.dataManager
				.getRootDirectory(ClientDataManager.subject);

		ClientDataManager.userPermissions = ClientDataManager.dataManager
				.getDefaultPermissions();
		ClientDataManager.DEFAULT_PERMISSIONS = ClientDataManager.dataManager
				.getDefaultPermissions();

		return new ClientPrimaryDataDirectory(rootDirectory, this);

	}

	/**
	 * Getter for the current {@link Subject}.
	 * 
	 * @return the subject
	 */
	protected Subject getSubject() {

		Subject ret;

		if ((ret = ClientDataManager.subject) == null) {
			ClientDataManager.logger.error("current subject is null");
		}
		return ret;
	}

	/**
	 * Getter all supported {@link Principal}s of the current eDAL system.
	 * 
	 * @return the list of supported {@link Principal}s
	 * @throws RemoteException
	 *             if unable to call remote function.
	 * @throws EdalException
	 *             if unable to load {@link Principal}s.
	 */
	public List<Class<? extends Principal>> getSupportedPrincipals()
			throws RemoteException, EdalException {
		return ClientDataManager.dataManager.getSupportedPrincipals();
	}

	/**
	 * Getter for the used space in the mount path of eDAL.
	 * 
	 * @return used space
	 * @throws RemoteException
	 *             if unable to call remote function.
	 * @throws EdalException
	 *             if no mount path is set.
	 */
	public Long getUsedStorageSpace() throws RemoteException, EdalException {
		return ClientDataManager.dataManager.getUsedStorageSpace();
	}

	/**
	 * Reset the initial default permissions.
	 */
	public void resetDefaultPermissions() {
		ClientDataManager.userPermissions = ClientDataManager.DEFAULT_PERMISSIONS;
	}

	/**
	 * Overrides the current default permissions of the current user with the
	 * new permissions.
	 * 
	 * @param permissions
	 *            the permissions to store.
	 */
	public void setDefaultPermissions(Map<Principal, List<Methods>> permissions) {
		ClientDataManager.userPermissions = permissions;
	}
}